/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.inspur.edp.lcm.metadata.core;

import com.inspur.edp.lcm.metadata.api.entity.DbConnectionInfo;
import com.inspur.edp.lcm.metadata.api.entity.DebugEnvironment;
import com.inspur.edp.lcm.metadata.api.entity.DeploymentContext;
import com.inspur.edp.lcm.metadata.api.entity.ExtractContext;
import com.inspur.edp.lcm.metadata.api.entity.GspProject;
import com.inspur.edp.lcm.metadata.api.entity.MetadataCompilerContext;
import com.inspur.edp.lcm.metadata.api.entity.compiler.MetadataCompilerConfiguration;
import com.inspur.edp.lcm.metadata.api.entity.extract.ExtractConfigration;
import com.inspur.edp.lcm.metadata.common.FileServiceImp;
import com.inspur.edp.lcm.metadata.common.Utils;
import com.inspur.edp.lcm.metadata.common.compiler.MetadataCompilerHelper;
import com.inspur.edp.lcm.metadata.common.deployer.DeploymentHelper;
import com.inspur.edp.lcm.metadata.common.extractor.ExtractHelper;
import com.inspur.edp.lcm.metadata.common.util.CommandUtil;
import com.inspur.edp.lcm.metadata.core.thread.ProcessRunnable;
import com.inspur.edp.lcm.metadata.spi.ExtractAction;
import com.inspur.edp.lcm.metadata.spi.MetadataCompileAction;
import com.inspur.edp.lcm.project.deployer.spi.DeployAction;
import io.iec.edp.caf.common.environment.EnvironmentUtil;
import io.iec.edp.caf.sumgr.core.ServiceUnitFactory;
import java.io.File;
import java.io.IOException;
import java.net.URLDecoder;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;
import sun.misc.BASE64Decoder;

/**
 * @author zhaoleitr
 */
public class ProjectExtendCoreService {
    private final FileServiceImp fileService = new FileServiceImp();
    private final MetadataProjectCoreService metadataProjectCoreService = new MetadataProjectCoreService();
    private final GspProjectCoreService gspProjectCoreService = new GspProjectCoreService();

    public String getDeployStatus(String sign) throws IOException {
        BASE64Decoder decoder = new BASE64Decoder();
        byte[] bytes = decoder.decodeBuffer(sign);
        String urlEncodeSign = new String(bytes);
        String fileName = URLDecoder.decode(urlEncodeSign, "utf8");
        String status = "";
        if (fileService.isFileExist(fileName)) {
            status = fileService.fileRead(fileName);
            switch (status) {
                case "success":
                case "fail":
                    fileService.fileDelete(fileName);
                    break;
                default:
                    break;
            }
        }
        return status;
    }

    public void extract(String absolutePath) {
        List<String> projPaths = new ArrayList<>();
        if (absolutePath.endsWith(Utils.getMetadataProjPath())) {
            absolutePath = new File(absolutePath).getParent();
        }
        metadataProjectCoreService.getProjPathsInPath(absolutePath, projPaths);
        for (String projPath : projPaths) {
            GspProject gspProjectInfo = gspProjectCoreService.getGspProjectInfo(projPath);
            ExtractContext extractContext = new ExtractContext();
            extractContext.setProjectPath(projPath);
            extractContext.setDeployPath(gspProjectInfo.getSuDeploymentPath());
            doExtract(extractContext);
        }
    }

    private void doExtract(ExtractContext extractContext) {
        // 文件清理
        clearPublishDir(extractContext);
        // 组织上下文
        buildExtractContext(extractContext);
        // 各类型提取扩展
        List<ExtractConfigration> extractConfigrationList = ExtractHelper.getInstance().getExtractConfigrationList();
        extractConfigrationList.forEach(item -> {
            if (item.isEnable()) {
                if (extractContext.getDeployType() != null && "WEB".equals(item.getTypeCode()) && !"all".equals(extractContext.getDeployType()) && !extractContext.getDeployType().contains("web")) {
                    return;
                }
                ExtractAction extractAction = ExtractHelper.getInstance().getManager(item.getTypeCode());
                if (extractAction == null) {
                    throw new RuntimeException("未能获取到" + item.getTypeCode() + "的提取器，请确认配置是否正确");
                }
                extractAction.extract(extractContext);
            }
        });
    }

    private void buildExtractContext(ExtractContext context) {
        String publishPath = Paths.get(context.getProjectPath()).resolve(Utils.getPublishPath()).toString();
        String deployPath = Paths.get(publishPath).resolve(Utils.getJstackPath()).resolve(context.getDeployPath().toLowerCase()).toString();
        context.setDeployPath(deployPath);
    }

    private void clearPublishDir(ExtractContext extractContext) {
        String publishPath = Paths.get(extractContext.getProjectPath()).resolve(Utils.getPublishPath()).toString();
        File file = new File(publishPath);
        if (!file.exists()) {
            file.mkdir();
        }
        try {
            fileService.cleanDirectory(file);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void deploy(DbConnectionInfo dBConnectionInfo, String serverPath, String projPath, String restart) {
        serverPath = serverPath == null || serverPath.isEmpty() ? Utils.getBasePath() : serverPath;

        beforeDeploy(serverPath, projPath);

        String args = handleArgs(dBConnectionInfo, serverPath, projPath, restart);
        String currentServerPath = Paths.get(Utils.getBasePath()).resolve("tools/deploy/project_java/runtime").toString();
        String javaHome = CommandUtil.getJavaHome(false);
        String javaCmd = javaHome == "" ? "java" : javaHome + "/java";
        String serverRuntimePathName = EnvironmentUtil.getServerRuntimePathName();
        String command = javaCmd + " -server \"-Dloader.path=libs,3rd\" \"-Dserver.runtime.path.name=" + serverRuntimePathName + "\" \"-Dspring.config.location=../config/application.yaml\" -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n  -jar lcm-project-deploy-tool.jar" + args;
        if ("0".equals(restart)) {
            newProcessRunnable(currentServerPath, command);
        } else {
            CommandUtil.newProcess(currentServerPath, command);
        }
    }

    private void beforeDeploy(String serverPath, String projPath) {
        if (!suFileExists(serverPath, projPath)) {
            generateSuFile(serverPath, projPath);
        }
    }

    private boolean suFileExists(String serverPath, String projPath) {
        GspProject gspProjectInfo = gspProjectCoreService.getGspProjectInfo(projPath);
        String serverLink = new File(FileServiceImp.combinePath(serverPath, "jstack")).exists() ? "jstack" : "server";
        String destDir = FileServiceImp.combinePath(serverPath, serverLink, "apps", gspProjectInfo.getAppCode().toLowerCase(), gspProjectInfo.getServiceUnitCode().toLowerCase());
        File file = new File(destDir);
        file.mkdirs();
        Boolean existsFlag = ServiceUnitFactory.createGenerator().exists(file);
        return existsFlag;
    }

    private void generateSuFile(String serverPath, String projPath) {
        GspProject gspProjectInfo = gspProjectCoreService.getGspProjectInfo(projPath);
        io.iec.edp.caf.sumgr.api.entity.ServiceUnitInfo serviceUnitInfo = new io.iec.edp.caf.sumgr.api.entity.ServiceUnitInfo();
        serviceUnitInfo.setApplicationName(gspProjectInfo.getAppCode());
        serviceUnitInfo.setName(gspProjectInfo.getServiceUnitCode());
        String serverLink = new File(FileServiceImp.combinePath(serverPath, "jstack")).exists() ? "jstack" : "server";
        String destDir = FileServiceImp.combinePath(serverPath, serverLink, "apps", gspProjectInfo.getAppCode().toLowerCase(), gspProjectInfo.getServiceUnitCode().toLowerCase());
        File file = new File(destDir);
        ServiceUnitFactory.createGenerator().generate(serviceUnitInfo, file);
    }

    private void newProcessRunnable(String serverPath, String command) {
        ProcessRunnable processRunnable = new ProcessRunnable();
        processRunnable.setServerPath(serverPath);
        processRunnable.setCommand(command);
        ExecutorService executor = Executors.newCachedThreadPool();
        executor.submit(processRunnable::run);
    }

    private String handleArgs(DbConnectionInfo dbConnectionInfo, String serverPath, String projPath, String restart) {
        StringBuilder stringBuilder = new StringBuilder();
        String publishPath = Paths.get(metadataProjectCoreService.getProjPath(projPath)).resolve(Utils.getPublishPath()).toString();
        appendArg(stringBuilder, publishPath);
        appendArg(stringBuilder, serverPath);
        appendArg(stringBuilder, dbConnectionInfo.getDbServerIP());
        appendArg(stringBuilder, dbConnectionInfo.getDbPort());
        appendArg(stringBuilder, dbConnectionInfo.getDbName());
        appendArg(stringBuilder, dbConnectionInfo.getDbUserName());
        appendArg(stringBuilder, dbConnectionInfo.getDbPassword());
        appendArg(stringBuilder, String.valueOf(dbConnectionInfo.getDbType().ordinal()));
        appendArg(stringBuilder, restart);

        if (restart.equals("0")) {
            int currentPid = CommandUtil.getCurrentPid();
            appendArg(stringBuilder, String.valueOf(currentPid));
        }

        return stringBuilder.toString();
    }

    private void appendArg(StringBuilder stringBuilder, String arg) {
        stringBuilder.append(" \"");
        stringBuilder.append(arg);
        stringBuilder.append("\"");
    }

    public void migration(String absolutePath) {
        DeploymentContext context = new DeploymentContext();
        String publishPath = Paths.get(metadataProjectCoreService.getProjPath(absolutePath)).resolve(Utils.getPublishPath()).toString();
        context.setPublishPath(publishPath);
        DebugEnvironment debugEnvironment = new DebugEnvironment();
        debugEnvironment.setServerPath(Utils.getBasePath());
        context.setDebugEnvironment(debugEnvironment);
        DeployAction baseMigration = DeploymentHelper.getInstance().getManager("BaseMigration");
        if (baseMigration == null) {
            throw new RuntimeException("未能获取到BaseMigration的部署器，请确认配置是否正确");
        }
        baseMigration.deploy(context);
    }

    public List<String> batchCompile(String absolutePath, String exts, String disabledExts, String packagePath) {
        List<String> projPaths = new ArrayList<>();
        if (absolutePath.contains(",")) {
            projPaths = Arrays.asList(absolutePath.split(","));
        } else {
            if (absolutePath.endsWith(Utils.getMetadataProjPath())) {
                absolutePath = new File(absolutePath).getParent();
            }
            metadataProjectCoreService.getProjPathsInPath(absolutePath, projPaths);
        }

        List<String> extsList = exts.isEmpty() ? new ArrayList<>() : Arrays.asList(exts.split(","));
        List<String> disabledExtsList = disabledExts.isEmpty() ? new ArrayList<>() : Arrays.asList(disabledExts.split(","));

        for (String projPath : projPaths) {
            //todo
//            "正在编译：" + projPath
            String projectMetadataPath = Paths.get(projPath).resolve(Utils.getMetadataProjPath()).toString();
            MetadataCompilerContext metadataCompilerContext = new MetadataCompilerContext();
            metadataCompilerContext.setProjectPath(projectMetadataPath);
            compile(metadataCompilerContext, extsList, disabledExtsList);
            //todo
//            "编译完成：" + projPath
        }

        return null;
    }

    private void compile(MetadataCompilerContext metadataCompilerContext, List<String> extsList,
        List<String> disabledExtsList) {
        List<MetadataCompilerConfiguration> compileConfigrationList = MetadataCompilerHelper.getInstance().getCompileConfigrationList();
        if (extsList != null && extsList.size() > 0) {
            compileConfigrationList = compileConfigrationList.stream().filter(config -> extsList.contains(config.getTypeCode())).collect(Collectors.toList());
        }
        if (disabledExtsList != null && disabledExtsList.size() > 0) {
            compileConfigrationList = compileConfigrationList.stream().filter(config -> !disabledExtsList.contains(config.getTypeCode())).collect(Collectors.toList());
        }
        for (MetadataCompilerConfiguration item : compileConfigrationList) {
            if (item.getEnable()) {
                MetadataCompileAction manager = MetadataCompilerHelper.getInstance().getManager(item.getTypeCode());
                if (manager != null) {
                    manager.metadataCompile(metadataCompilerContext);
                } else {
                    //todo
//                    item.getTypeCode() + "无法获取类"
                }
            }
        }
    }
}
